package profile

import (
	"context"
	blobpb "coolcar/v1/blob/api/gen/v1"
	rentalpb "coolcar/v1/rental/api/gen/v1"
	"coolcar/v1/rental/profile/dao"
	"coolcar/v1/shared/auth"
	"coolcar/v1/shared/ids"
	"go.mongodb.org/mongo-driver/mongo"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"time"
)

type Service struct {
	BlobClient         blobpb.BlobServiceClient
	Sugar              *zap.SugaredLogger
	Mongo              *dao.Mongo
	PhotoGetExpired    time.Duration
	PhotoUploadExpired time.Duration
}

func (s *Service) GetProfilePhoto(ctx context.Context, request *rentalpb.GetProfilePhotoRequest) (*rentalpb.GetProfilePhotoResponse, error) {
	aid, err := auth.AccountIDFromContext(ctx)
	if err != nil {
		return nil, err
	}
	pr, err := s.Mongo.GetProfile(ctx, aid)
	if err != nil {
		return nil, status.Error(s.logAndConvertProfileErr(err), "")
	}
	if pr.PhotoBlobID == "" {
		return nil, status.Error(codes.NotFound, "")
	}
	br, err := s.BlobClient.GetBlobURL(ctx, &blobpb.GetBlobURLRequest{
		Id:         pr.PhotoBlobID,
		TimeoutSec: int32(s.PhotoGetExpired.Seconds()),
	})
	if err != nil {
		s.Sugar.Error("failed to get blob", zap.Error(err))
		return nil, status.Error(codes.Internal, "")
	}
	return &rentalpb.GetProfilePhotoResponse{Url: br.Url}, nil
}

func (s *Service) CreateProfilePhoto(ctx context.Context, request *rentalpb.CreateProfilePhotoRequest) (*rentalpb.CreateProfilePhotoResponse, error) {
	aid, err := auth.AccountIDFromContext(ctx)
	if err != nil {
		return nil, err
	}
	br, err := s.BlobClient.CreateBlob(ctx, &blobpb.CreateBlobRequest{
		AccountId:           aid.String(),
		UploadUrlTimeoutSec: int32(s.PhotoUploadExpired.Seconds()),
	})
	if err != nil {
		s.Sugar.Error("failed to create blob", zap.Error(err))
		return nil, status.Error(codes.Aborted, "")
	}
	err = s.Mongo.UpdateProfilePhoto(ctx, aid, ids.BlobID(br.Id))
	if err != nil {
		s.Sugar.Error("failed to create blob", zap.Error(err))
		return nil, status.Error(codes.Aborted, "")
	}
	return &rentalpb.CreateProfilePhotoResponse{
		UploadUrl: br.UploadUrl,
	}, err
}

func (s *Service) CompleteProfilePhoto(ctx context.Context, request *rentalpb.CompleteProfilePhotoRequest) (*rentalpb.CompleteProfilePhotoResponse, error) {
	aid, err := auth.AccountIDFromContext(ctx)
	if err != nil {
		return nil, err
	}
	pr, err := s.Mongo.GetProfile(ctx, aid)
	if err != nil {
		return nil, status.Error(s.logAndConvertProfileErr(err), "")
	}
	if pr.PhotoBlobID == "" {
		return nil, status.Error(codes.NotFound, "")
	}
	br, err := s.BlobClient.GetBlob(ctx, &blobpb.GetBlobRequest{
		Id: pr.PhotoBlobID,
	})
	if err != nil {
		s.Sugar.Error("failed to get blob", zap.Error(err))
		return nil, status.Error(codes.Aborted, "")
	}
	s.Sugar.Info("got profile photo", zap.Int("size", len(br.Date)))
	return &rentalpb.CompleteProfilePhotoResponse{
		Identity: &rentalpb.Identity{
			LicNumber:       "32222222",
			Name:            "lisi",
			Gender:          rentalpb.Gender_FEMALE,
			BirthDateMillis: 655555555555,
		},
	}, nil
}

func (s *Service) GetProfile(ctx context.Context, request *rentalpb.GetProfileRequest) (*rentalpb.Profile, error) {
	aid, err := auth.AccountIDFromContext(ctx)
	if err != nil {
		return nil, err
	}
	pr, err := s.Mongo.GetProfile(ctx, aid)
	if err != nil {
		code := s.logAndConvertProfileErr(err)
		if code == codes.NotFound {
			return &rentalpb.Profile{}, nil
		}
		return nil, status.Error(code, "")
	}
	if pr.Profile != nil {
		return &rentalpb.Profile{}, nil
	}
	return pr.Profile, nil
}

func (s *Service) SubmitProfile(ctx context.Context, identity *rentalpb.Identity) (*rentalpb.Profile, error) {
	aid, err := auth.AccountIDFromContext(ctx)
	if err != nil {
		return nil, err
	}
	p := &rentalpb.Profile{
		Identity:       identity,
		IdentityStatus: rentalpb.IdentityStatus_PENDING,
	}
	err = s.Mongo.UpdatedProfile(ctx, aid, rentalpb.IdentityStatus_UNSUBMITTED, p)
	if err != nil {
		s.Sugar.Error("failed to update profile", zap.Error(err))
		return nil, status.Errorf(codes.Internal, "")
	}
	return p, nil
}

func (s *Service) ClearProfile(ctx context.Context, request *rentalpb.ClearProfileRequest) (*rentalpb.Profile, error) {
	aid, err := auth.AccountIDFromContext(ctx)
	if err != nil {
		return nil, err
	}
	p := &rentalpb.Profile{}
	err = s.Mongo.UpdatedProfile(ctx, aid, rentalpb.IdentityStatus_VERIFIED, p)
	if err != nil {
		s.Sugar.Error("failed to update profile", zap.Error(err))
		return nil, status.Errorf(codes.Internal, "")
	}
	return p, nil
}

func (s *Service) logAndConvertProfileErr(err error) codes.Code {
	if err != mongo.ErrNoDocuments {
		return codes.NotFound
	}
	if err != nil {
		s.Sugar.Error("failed to convert profile", zap.Error(err))
		return codes.Internal
	}
	return codes.OK
}
